#include "utils/header_global.h"
#include <cstring>
#include <iostream>
#include <string.h>
#include <string>

// =============== Windows ==============
#if defined(_WIN32) || defined(_WIN64)
#include <cstdint>
#include <ws2tcpip.h>
#include <winsock2.h>
#include "ws2ipdef.h"
#include <windows.h>
#pragma comment(lib, "ws2_32.lib")
#define close(sock) closesocket(sock)
typedef SOCKET socket_t;
#define INVALID_SOCKET_VALUE INVALID_SOCKET
using ushort = unsigned short;
// =========  Unix/Linux/Macos  =========
#else
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <ifaddrs.h> //ipv4
#include <netdb.h>   //ipv4
#include <sys/select.h>
#define INVALID_SOCKET_VALUE -1
typedef int             socket_t;
typedef struct sockaddr SOCKADDR;
#endif

using namespace std;

#ifdef _WIN32
#include <windows.h>
#define close(sock) closesocket(sock)
#endif

class Broadcast {
private:
	int publisher_sockfd;
	int receiver_sockfd;

public:
	Broadcast() {}
	~Broadcast() {
		close(publisher_sockfd);
		close(receiver_sockfd);
	}

	// 【发布者】发布广播消息
	void static publish(const string &broadcastAddr, int port, const string &message) {
		int publisher_sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
		if (publisher_sockfd < 1) {
			return perror("socket creation failed");
		}

		// 设置广播选项
		int broadcastEnable = 1;
		if (setsockopt(publisher_sockfd, SOL_SOCKET, SO_BROADCAST, (char *) &broadcastEnable, sizeof(broadcastEnable)) < 0) {
			return perror("setsockopt failed");
		}

		struct sockaddr_in serveraddr;
		memset(&serveraddr, 0, sizeof(serveraddr));
		serveraddr.sin_family = AF_INET;
		serveraddr.sin_port   = htons(port);
		if (inet_pton(AF_INET, broadcastAddr.c_str(), &serveraddr.sin_addr) <= 0) {
			return perror("invalid address");
		}

		ssize_t sentBytes = sendto(publisher_sockfd, message.c_str(), message.length(), 0, (struct sockaddr *) &serveraddr, sizeof(serveraddr));
		if (sentBytes < 0) {
			return perror("sento failled");
		}
		cout << "Messaet sent: " << message << endl;
	}

	// 【监听器】监听广播
	void static listen(int port) {
		int                receiver_sockfd;
		struct sockaddr_in serverAddr, clientAddr;
		socklen_t          len = sizeof(clientAddr);
		char               buffer[1024];
		ssize_t            n;
		if ((receiver_sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { // NOLINT
			return perror("socket creation failed");
		}

		// 设置广播选项
#if _WIN32
		char broadcastEnable = 1;
		if (setsockopt(receiver_sockfd, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable)) == -1) { // SOCKET_ERROR
			return perror("broadcast setsockopt failed");
		}
#endif

		memset(&serverAddr, 0, sizeof(serverAddr));
		memset(&clientAddr, 0, sizeof(clientAddr));
		serverAddr.sin_family      = AF_INET;
		serverAddr.sin_addr.s_addr = INADDR_ANY;
		serverAddr.sin_port        = htons(port); // NOLINT
		if (::bind(receiver_sockfd, (const struct sockaddr *) &serverAddr, sizeof(serverAddr)) < 0) {
			return perror("broadcast bind failed");
		}
		std::cout << "broadcast receiver is listening on " << port << "..." << endl;
		while (true) {
			n = recvfrom(receiver_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *) &clientAddr, &len);
			if (n < 0) {
				perror("recvfrom failed");
				continue;
			}
			buffer[n] = '\n';
			char ipstr[INET_ADDRSTRLEN];
			inet_ntop(AF_INET, &clientAddr.sin_addr, ipstr, INET_ADDRSTRLEN);
			std::cout << "Received [" << ipstr << ":" << ntohs(clientAddr.sin_port) << "] => " << buffer << std::endl;
		}
	}
};
